﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using System.Data.SqlClient;
using System.Diagnostics;
using System.Linq;
using System.ServiceModel;
using DataService.CrudClassGenerator;
using Types.Attributes;
using Types.CustomTypes;
using Types.DataSets.Log;
using Types.Enumerations;
using DOA = DataService.DataObjectAdapter;
using DSU = DataService.DataServiceUtil;

namespace DataService.Dataset
{
    internal class DataObject : IDataObject
    {
        #region [MEMBERS] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

        public static string NoAuthorizationForIUD = "You do not have Authorization for Insert, Update and Delete process on this page!";

        private DbConnection mConnection;
        private DbTransaction mTransaction;
        private bool mIsTransactional;
        private static Hashtable mDataSetTypes;
        private static object mTsObject;
        private string mDefaultDB;

        private const IsolationLevel ISOLATION_LEVEL = IsolationLevel.ReadCommitted;

        #endregion

        #region [HELPER METHODS] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

        #region [STATIC METHODS] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

        static DataObject()
        {
            mTsObject = new object();
            mDataSetTypes = new Hashtable();
        }

        #endregion

        #region [PRIVATE METHODS] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

        private CDataSetMetaData GetDatasetMetadata(DataTable pDt)
        {

            // Custom tool disinda bir datatable... 
            if (pDt.GetType().IsDefined(typeof(FullTableNameAttribute), false) == false)
                return null;

            if (mDataSetTypes.ContainsKey(pDt.GetType().FullName))
                return (CDataSetMetaData)(mDataSetTypes[pDt.GetType().FullName]);

            lock (mTsObject)
            {
                if (mDataSetTypes.ContainsKey(pDt.GetType().FullName))
                    return (CDataSetMetaData)(mDataSetTypes[pDt.GetType().FullName]);

                CDataSetMetaData oMeta = new CDataSetMetaData();
                oMeta.dataSetTypeName = pDt.GetType().FullName;
                if (pDt.GetType().IsDefined(typeof(FullTableNameAttribute), false))
                {
                    FullTableNameAttribute dbAttribute = (FullTableNameAttribute)pDt.GetType().GetCustomAttributes(typeof(FullTableNameAttribute), false)[0];
                    oMeta.fullTableName = dbAttribute.FullTableName;
                }
                if (pDt.GetType().IsDefined(typeof(AutoIncColNameAttribute), false))
                {
                    AutoIncColNameAttribute autoAttribute = (AutoIncColNameAttribute)pDt.GetType().GetCustomAttributes(typeof(AutoIncColNameAttribute), false)[0];
                    oMeta.autoIncrementColumnName = autoAttribute.AutoIncColName;
                }

                // CommandBuilder kullanarak commandleri olustur ve bu commandleri cache'le. 
                DbCommand cmd = DOA.GetCommand(string.Format("SELECT * FROM {0}", oMeta.fullTableName));
                cmd.Connection = mConnection;
                DbDataAdapter da = DOA.GetDataAdapter(cmd);
                DbCommandBuilder mCommandBuilder = DOA.GetCommandBuilder(da);
                mCommandBuilder.SetAllValues = false;   //Added to send only changed parameters to database during update process
                if (mIsTransactional)
                    da.SelectCommand.Transaction = mTransaction;

                //Computed control
                bool containsComputed = false;
                string insertCommandTextUpper = string.Empty;
                string insertCommandTextValues = string.Empty;

                string updateCommandTextUpper = string.Empty;
                string updateCommandTextValues = string.Empty;

                DbCommand insertCommand = DOA.GetCommand();
                DbCommand updateCommand = DOA.GetCommand();

                for (int i = 0; i < pDt.Columns.Count; i++)
                {
                    DataColumn dc = pDt.Columns[i];

                    if (!dc.Unique && dc.ReadOnly)
                        containsComputed = true;
                    else
                    {
                        if (!dc.AutoIncrement)
                        {
                            #region InsertCommand
                            insertCommandTextUpper += string.Format("{0} {1}", !String.IsNullOrEmpty(updateCommandTextUpper) ? "," : string.Empty, dc.ColumnName);
                            insertCommandTextValues += string.Format("{0} @{1}", !String.IsNullOrEmpty(insertCommandTextValues) ? "," : string.Empty, dc.ColumnName);
                            insertCommand.Parameters.Add(DOA.GetParameter("@", dc));
                            #endregion
                        }
                        #region UpdateCommand

                        if (!dc.AutoIncrement)
                        {
                            updateCommandTextUpper += string.Format("{0} {1} = @{1}", !String.IsNullOrEmpty(updateCommandTextUpper) ? "," : string.Empty, dc.ColumnName);
                        }

                        //update command values
                        if (dc.AllowDBNull)
                        {
                            updateCommandTextValues += String.Format(" {1} ((@IsNull_{0} = 1 AND [{0}] IS NULL) OR ([{0}] =@Original_{0}))", dc.ColumnName, (!String.IsNullOrEmpty(updateCommandTextValues) ? "AND" : ""));
                            updateCommand.Parameters.Add(DOA.GetParameter("@", dc));
                            updateCommand.Parameters.Add(DOA.GetParameter("@IsNull_", dc));
                            updateCommand.Parameters.Add(DOA.GetParameter("@Original_", dc));
                        }
                        else
                        {
                            updateCommandTextValues += String.Format(" {1} ([{0}] = @Original_{0})", dc.ColumnName, (!String.IsNullOrEmpty(updateCommandTextValues) ? "AND" : ""));
                            updateCommand.Parameters.Add(DOA.GetParameter("@", dc));
                            updateCommand.Parameters.Add(DOA.GetParameter("@Original_", dc));
                        }
                        #endregion
                    }
                }

                insertCommandTextUpper = string.Format("INSERT INTO {0} ({1})", oMeta.fullTableName, insertCommandTextUpper);
                insertCommandTextValues = string.Format(" VALUES ({0})", insertCommandTextValues);

                DbCommand cmdInsertCommand;

                if (!containsComputed)
                    cmdInsertCommand = mCommandBuilder.GetInsertCommand();
                else
                {
                    insertCommand.CommandText = insertCommandTextUpper + insertCommandTextValues;
                    insertCommand.Connection = cmd.Connection;
                    insertCommand.Transaction = cmd.Transaction;
                    cmdInsertCommand = insertCommand;
                }

                oMeta.insertCommand = cmdInsertCommand.CommandText;
                if (!String.IsNullOrEmpty(oMeta.autoIncrementColumnName))
                {
                    oMeta.insertCommand += string.Format(";{0} WHERE {1} = SCOPE_IDENTITY();", cmd.CommandText, oMeta.autoIncrementColumnName);
                }
                oMeta.insertParameterList = new SqlParameter[cmdInsertCommand.Parameters.Count];
                cmdInsertCommand.Parameters.CopyTo(oMeta.insertParameterList, 0);

                updateCommandTextUpper = string.Format("UPDATE {0} SET {1}", oMeta.fullTableName, updateCommandTextUpper); ;
                updateCommandTextValues = string.Format(" WHERE ({0})", updateCommandTextValues);

                string updateCommandText = string.Format("{0}{1}", updateCommandTextUpper, updateCommandTextValues);
                if (pDt.PrimaryKey != null)
                {
                    DataColumn[] columnArray = pDt.PrimaryKey;
                    string updateCommandWhereClause = string.Empty;
                    foreach (var column in columnArray)
                    {
                        updateCommandWhereClause = string.Format("{1} {0} = @{0}", column.ColumnName, !String.IsNullOrEmpty(updateCommandWhereClause) ? "AND" : string.Empty);
                    }

                    string updateCommandSelectText = string.Format(";SELECT * FROM {0} WHERE {1};", oMeta.fullTableName, updateCommandWhereClause);
                    updateCommandText = string.Format("{0}{1}", updateCommandText, updateCommandSelectText);
                }

                DbCommand cmdUpdateCommand;
                if (!containsComputed)
                    cmdUpdateCommand = mCommandBuilder.GetUpdateCommand();
                else
                {
                    updateCommand.CommandText = updateCommandText;
                    updateCommand.Connection = cmd.Connection;
                    updateCommand.Transaction = cmd.Transaction;
                    cmdUpdateCommand = updateCommand;
                }

                oMeta.updateCommand = cmdUpdateCommand.CommandText;
                oMeta.updateParameterList = new SqlParameter[cmdUpdateCommand.Parameters.Count];
                cmdUpdateCommand.Parameters.CopyTo(oMeta.updateParameterList, 0);

                DbCommand cmdDeleteCommand = mCommandBuilder.GetDeleteCommand();
                oMeta.deleteCommand = cmdDeleteCommand.CommandText;
                oMeta.deleteParameterList = new SqlParameter[cmdDeleteCommand.Parameters.Count];
                cmdDeleteCommand.Parameters.CopyTo(oMeta.deleteParameterList, 0);


                mDataSetTypes.Add(pDt.GetType().FullName, oMeta);
                return oMeta;
            }
        }

        private void BeginTransaction()
        {
            mTransaction = mConnection.BeginTransaction(ISOLATION_LEVEL);
        }

        private void OpenConnection()
        {
            if (mIsTransactional == false)
            {
                mConnection.Open();
            }
        }

        private void CloseConnection()
        {
            if (mIsTransactional == false)
            {
                mConnection.Close();
            }
        }

        private void WriteAdHocQueryLog(DbCommand pCommand, bool pIsTransactional, string pFullAdHocQueryLogTableName = "")
        {
            CCGLog log = new CCGLog();
            if (!string.IsNullOrEmpty(mDefaultDB)) log.FullAdHocQueryLogTableName = pFullAdHocQueryLogTableName;
            DbCommand cmd = log.GetAdHocQueryLogCommand(pCommand.Parameters);
            pCommand.MergeCommands(cmd);
            if (!pIsTransactional) pCommand.AddTransaction();
        }

        #endregion

        #region [PUBLIC METHODS] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

        public DataObject(string pConnectionString, string pDefaultDB, bool pIsTransactional)
        {
            if (!String.IsNullOrEmpty(pDefaultDB))
            {
                mDefaultDB = pDefaultDB;
            }
            mIsTransactional = pIsTransactional;

            mConnection = DOA.GetDbConnection(pConnectionString, mDefaultDB);

            if (pIsTransactional)
            {
                mConnection.Open();
                BeginTransaction();
            }
        }

        public bool IsDataObjectTransactional
        {
            get { return mIsTransactional; }
        }

        public void CommitTransaction()
        {
            if (mIsTransactional)
            {
                mTransaction.Commit();
                mConnection.Close();
            }
        }

        public void RollbackTransaction()
        {
            if (mIsTransactional)
            {
                mTransaction.Rollback();
                mConnection.Close();
            }
        }

        private string ProcessorUserID
        {
            get
            {
                string processorId = string.Empty;

                // Get processor person unique id

                return processorId;
            }
        }

        private bool PageReadOnly
        {
            get
            {
                if (GetContextData() != null)
                {
                    string readOnly = GetContextData().PageReadOnly;
                    return readOnly == YesNoStr.Yes ? true : false;
                }
                return false;
            }
        }

        public CContextMetaData GetContextData()
        {
            CContextMetaData mContextMetaData = null;

            if (OperationContext.Current == null || OperationContext.Current.IncomingMessageHeaders == null)
            {
                mContextMetaData = new CContextMetaData();
            }
            else
            {
                int contextHeaderIndex = OperationContext.Current.IncomingMessageHeaders.FindHeader(ContextParameters.ContextMetaDataName, ContextParameters.ContextMetaDataNamespace);
                if (contextHeaderIndex > 0)
                {
                    mContextMetaData = OperationContext.Current.IncomingMessageHeaders.GetHeader<CContextMetaData>(contextHeaderIndex);
                }
            }
            return mContextMetaData;
        }

        #endregion

        #endregion

        #region [EXECUTE] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

        public int ExecuteNonQueryStatement(DbCommand pCommand)
        {
            return ExecuteNonQueryStatement(pCommand, false);
        }
        public int ExecuteNonQueryStatement(DbCommand pCommand, bool pLogProcess)
        {
            if (PageReadOnly) throw new ApplicationException(NoAuthorizationForIUD);

            try
            {
                if (pLogProcess) WriteAdHocQueryLog(pCommand, mIsTransactional);
                if (mIsTransactional) pCommand.Transaction = mTransaction;

                OpenConnection();
                pCommand.Connection = mConnection;

                int mRowsAffected = pCommand.ExecuteNonQuery();
                return mRowsAffected;
            }
            catch (Exception ex)
            {
                HandleException(ex, pCommand);
                return 0;
            }
            finally
            {
                CloseConnection();
            }
        }

        public object ExecuteScalar(DbCommand pCommand)
        {
            return ExecuteScalar(pCommand, false);
        }
        public object ExecuteScalar(DbCommand pCommand, bool pIslemiLogla)
        {
            try
            {
                if (pIslemiLogla) WriteAdHocQueryLog(pCommand, mIsTransactional);
                if (mIsTransactional) pCommand.Transaction = mTransaction;

                OpenConnection();
                pCommand.Connection = mConnection;

                object oReturn = pCommand.ExecuteScalar();
                return oReturn;
            }
            catch (Exception ex)
            {
                HandleException(ex, pCommand);
                return null;
            }
            finally
            {
                CloseConnection();
            }
        }

        #endregion

        #region [GET RECORDS] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

        /// <summary>
        /// If table and column names are different from Dataset, this method throws exception.
        /// For Schema independent process, call other overload method with true.
        /// </summary>
        public void GetRecords(DataTable pDataTable, DbCommand pCommand)
        {
            GetRecords(pDataTable, pCommand, false);
        }

        public void GetRecords(DataTable pDataTable, DbCommand pCommand, bool pIgnoreSchema)
        {
            GetRecords(pDataTable, pCommand, pIgnoreSchema, null);
        }

        public void GetRecords(DataTable pDataTable, DbCommand pCommand, bool pIgnoreSchema, LoadOption? pLoadOption)
        {
            string tableName = "Table1";
            OpenConnection();
            pCommand.Connection = mConnection;
            DbDataAdapter da = DOA.GetDataAdapter(pCommand);

            if (mIsTransactional)
            {
                da.SelectCommand.Transaction = mTransaction;
            }
            try
            {
                if (!pIgnoreSchema && !String.IsNullOrEmpty(pDataTable.TableName) && pDataTable.TableName != tableName)
                    da.MissingSchemaAction = MissingSchemaAction.Error;

                if (pLoadOption.HasValue)
                    da.FillLoadOption = pLoadOption.Value;

                da.Fill(pDataTable);

                DSU.SetTableName(pDataTable);
            }
            catch (Exception ex)
            {
                HandleException(ex, pCommand);
            }
            finally
            {
                CloseConnection();
            }
        }

        public void GetRecords(DataSet pDataSet, DbCommand pCommand)
        {
            GetRecords(pDataSet, pCommand, false);
        }

        public void GetRecords(DataSet pDataSet, DbCommand pCommand, bool pIgnoreSchema)
        {
            GetRecords(pDataSet, pCommand, pIgnoreSchema, null);
        }

        public void GetRecords(DataSet pDataSet, DbCommand pCommand, bool pIgnoreSchema, LoadOption? pLoadOption)
        {
            OpenConnection();
            pCommand.Connection = mConnection;
            DbDataAdapter da = DOA.GetDataAdapter(pCommand);

            if (mIsTransactional)
            {
                da.SelectCommand.Transaction = mTransaction;
            }
            try
            {
                for (int i = 0; i < pDataSet.Tables.Count; i++)
                {
                    if (!pIgnoreSchema && !String.IsNullOrEmpty(pDataSet.Tables[i].TableName) && pDataSet.Tables[i].TableName != string.Format("Table{0}", i))
                        da.MissingSchemaAction = MissingSchemaAction.Error;
                }

                if (pLoadOption.HasValue)
                    da.FillLoadOption = pLoadOption.Value;

                da.Fill(pDataSet);

                DSU.SetTableName(pDataSet);
            }
            catch (Exception ex)
            {
                HandleException(ex, pCommand);
            }
            finally
            {
                CloseConnection();
            }
        }

        #endregion

        #region [UPDATE] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

        //--- For non typed DataSets

        public void Update(DataTable pDataTable, DbCommand pCommand)
        {
            Update(pDataTable, pCommand, ConflictOption.CompareAllSearchableValues, false);
        }

        public void Update(DataTable pDataTable, DbCommand pCommand, bool pLogProcess)
        {
            Update(pDataTable, pCommand, ConflictOption.CompareAllSearchableValues, pLogProcess);
        }

        public void Update(DataTable pDataTable, DbCommand pCommand, ConflictOption pConflictOption)
        {
            Update(pDataTable, pCommand, pConflictOption, false);
        }

        public void Update(DataTable pDataTable, DbCommand pCommand, ConflictOption pConflictOption, bool pLogProcess)
        {
            if (PageReadOnly) throw new ApplicationException(NoAuthorizationForIUD);

            OpenConnection();
            pCommand.Connection = mConnection;
            DbDataAdapter da = DOA.GetDataAdapter(pCommand);
            DbDataAdapter daIdent = DOA.GetDataAdapter(pCommand);
            if (mIsTransactional)
            {
                da.SelectCommand.Transaction = mTransaction;
            }
            try
            {
                DbCommandBuilder mCommandBuilder = DOA.GetCommandBuilder(da);
                mCommandBuilder.SetAllValues = false;   //Added to send only changed parameters to database during update process
                mCommandBuilder.ConflictOption = pConflictOption;

                string sIdentityColumnName = GetIdentityColumnName(pDataTable);
                DbCommand mInsertCommand = mCommandBuilder.GetInsertCommand();
                if (!String.IsNullOrEmpty(sIdentityColumnName))
                {
                    mInsertCommand.CommandText += string.Format(";{0} WHERE {1} = SCOPE_IDENTITY();", pCommand.CommandText, sIdentityColumnName);
                }
                mInsertCommand.UpdatedRowSource = UpdateRowSource.Both;
                daIdent.InsertCommand = mInsertCommand;
                daIdent.UpdateCommand = mCommandBuilder.GetUpdateCommand();
                daIdent.DeleteCommand = mCommandBuilder.GetDeleteCommand();

                if (IsLoggingTrue(pDataTable, pLogProcess))
                {
                    LogChanges(pDataTable, daIdent);
                }
                else
                {
                    daIdent.Update(pDataTable);
                }
            }
            catch (Exception ex)
            {
                HandleException(ex, pDataTable);
            }
            finally
            {
                CloseConnection();
            }
        }

        //--- For Typed DataSets

        public void Update(DataTable pDataTable)
        {
            Update(pDataTable, true, false, false);
        }

        public void Update(DataTable pDataTable, bool pLogProcess)
        {
            Update(pDataTable, true, false, pLogProcess);
        }

        public void Update(DataTable pDataTable, bool pAcceptChangesDuringUpdate, bool pContinueUpdateonError)
        {
            Update(pDataTable, pAcceptChangesDuringUpdate, pContinueUpdateonError, false);
        }

        public void Update(DataTable pDataTable, bool pAcceptChangesDuringUpdate, bool pContinueUpdateonError, bool pLogProcess)
        {
            if (PageReadOnly) throw new ApplicationException(NoAuthorizationForIUD);

            OpenConnection();
            try
            {
                CDataSetMetaData oMeta = GetDatasetMetadata(pDataTable);

                if (oMeta == null)
                    throw new NotSupportedException("Standart datatable, diger update metodunu kullan.");

                DbDataAdapter da = DOA.GetDataAdapter();
                da.InsertCommand = DOA.GetCommand(oMeta.insertCommand, mConnection);
                da.InsertCommand.UpdatedRowSource = UpdateRowSource.Both;
                foreach (SqlParameter p in oMeta.insertParameterList)
                {
                    da.InsertCommand.Parameters.Add((SqlParameter)((ICloneable)p).Clone());
                }
                da.UpdateCommand = DOA.GetCommand(oMeta.updateCommand, mConnection);
                foreach (SqlParameter p in oMeta.updateParameterList)
                {
                    da.UpdateCommand.Parameters.Add((SqlParameter)((ICloneable)p).Clone());
                }

                da.DeleteCommand = DOA.GetCommand(oMeta.deleteCommand, mConnection);
                foreach (SqlParameter p in oMeta.deleteParameterList)
                {
                    da.DeleteCommand.Parameters.Add((SqlParameter)((ICloneable)p).Clone());
                }

                if (mIsTransactional)
                {
                    da.InsertCommand.Transaction = mTransaction;
                    da.UpdateCommand.Transaction = mTransaction;
                    da.DeleteCommand.Transaction = mTransaction;
                }

                da.ContinueUpdateOnError = pContinueUpdateonError;
                da.AcceptChangesDuringUpdate = pAcceptChangesDuringUpdate;

                if (IsLoggingTrue(pDataTable, pLogProcess))
                {
                    LogChanges(pDataTable, da);
                }
                else
                {
                    da.Update(pDataTable);
                }
            }
            catch (Exception ex)
            {
                HandleException(ex, pDataTable);
                throw ex;
            }
            finally
            {
                CloseConnection();
            }
        }

        //--- Log control

        private bool IsLoggingTrue(DataTable pDataTable, bool pLogProcess)
        {
            if (pDataTable.TableName != "FullLogTableName".ReadAppSetting<string>()
                            && pDataTable.TableName != "FullAdHocQueryLogTableName".ReadAppSetting<string>()
                            && pDataTable.TableName != "FullExceptionLogTableName".ReadAppSetting<string>()
                            && pDataTable.GetChanges() != null
                            && pLogProcess)
            {
                return true;
            }
            else
            {
                return false;
            }
        }

        #endregion

        #region [LOG] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

        private const string DEFAULT_DB_NAME = "DEFAULT_DB_NAME";
        private const string DB_NAME_OTHER1 = "DB_NAME_OTHER1";
        private const string DB_NAME_OTHER2 = "DB_NAME_OTHER2";

        private void LogChanges(DataTable pDataTable, DbDataAdapter pDbDataAdapter)
        {
            /* DESCRIPTION:
             * Changed rows are logged and also DataTable object is sent to database in this method.
             * 
             * AÇIKLAMA: 
             * Bir DataTable'da yapılan değişiklikler log'lanacak ise bu method'a gelir ve aynı zamanda veritabanına bu method içinde gönderilir
             */

            DataSet logDs = GetLogDataSet();
            DataTable oDt = new DataTable();

            string pkValueColumnName = string.Empty;

            if (pDataTable.GetChanges(DataRowState.Added) != null)
            {
                oDt = pDataTable.GetChanges(DataRowState.Added);
                DataColumn[] dtClmn = pDataTable.PrimaryKey;
                if (dtClmn[0].AutoIncrement) pkValueColumnName = dtClmn[0].ColumnName;

                AddChangedRowsToLogDataSet(pDataTable, oDt, logDs, "I");
            }
            if (pDataTable.GetChanges(DataRowState.Modified) != null)
            {
                DataView oDvw = new DataView(pDataTable, string.Empty, string.Empty, DataViewRowState.ModifiedOriginal);
                oDt = oDvw.ToTable();
                AddChangedRowsToLogDataSet(pDataTable, oDt, logDs, "U");
            }
            if (pDataTable.GetChanges(DataRowState.Deleted) != null)
            {
                DataView oDvw = new DataView(pDataTable, string.Empty, string.Empty, DataViewRowState.Deleted);
                oDt = oDvw.ToTable();
                AddChangedRowsToLogDataSet(pDataTable, oDt, logDs, "D");
            }

            pDbDataAdapter.Update(pDataTable);
            CloseConnection();

            ChcekInsertedRowsAndUpdatePrimaryKeys(pDataTable, logDs, pkValueColumnName);

            this.Update(GetLogDataTable(logDs));
        }

        private static void ChcekInsertedRowsAndUpdatePrimaryKeys(DataTable pDataTable, DataSet logDs, string pkValueColumnName)
        {
            /* DESCRIPTION: 
             * If a DataTable object includes inserted rows and PrimariKey is AutoIncrement then,
             * this method is used to get primary key after an insert process and to send this key to Log table
             * 
             * AÇIKLAMA: 
             * Veritabanına gönderilen bir DataTable içinde Inteserted satırlar varsa ve PrimariKey alanı AutoIncrement ise,
             * Yeni kayıt ile birlikte oluşan PrimaryKey değerini almak ve LOG tablosuna göndermek için alttaki işlemler yapılır
             */

            if (!string.IsNullOrEmpty(pkValueColumnName))
            {
                foreach (DataRow dtRw in pDataTable.Rows)
                {
                    string iudValues = string.Empty;
                    foreach (DataColumn dtClmn in pDataTable.Columns)
                    {
                        if (dtClmn.ColumnName == pkValueColumnName) continue;
                        iudValues = string.Format("{0},{1}={2}", iudValues, dtClmn.ColumnName, dtRw[dtClmn].ToString());
                    }
                    iudValues = iudValues.TrimStart(',');

                    foreach (DataRow dRwLog in logDs.Tables[0].Rows)
                    {
                        if (dRwLog["CRUDVALUES"].ToString() == iudValues)
                        {
                            dRwLog["PKVALUE"] = dtRw[pkValueColumnName];
                            dRwLog["CRUDVALUES"] = iudValues;
                            break;
                        }
                    }
                }
            }
        }

        private DataSet GetLogDataSet()
        {
            switch (mDefaultDB)
            {
                case DEFAULT_DB_NAME: return new CRUD_LOGDataSet();
                case DB_NAME_OTHER1: return null;
                case DB_NAME_OTHER2: return null;
                default: return null;
            }
        }
        private DataTable GetLogDataTable(DataSet pLogDs)
        {
            switch (mDefaultDB)
            {
                case DEFAULT_DB_NAME: return ((CRUD_LOGDataSet)pLogDs).CRUD_LOG;
                case DB_NAME_OTHER1: return null;
                case DB_NAME_OTHER2: return null;
                default: return null;
            }
        }

        /// <summary>
        /// Used to insert changed records to log table
        /// </summary>
        /// <param name="pDataTable">Original DataTable object</param>
        /// <param name="pDt">DataTable object which includes cahnged records</param>
        /// <param name="pLogDs"></param>
        /// <param name="pCrudType">Crud type. Insert(I), Update(U) and Delete(D)</param>
        private void AddChangedRowsToLogDataSet(DataTable pDataTable, DataTable pDt, DataSet pLogDs, string pCrudType)
        {
            //int clmnCount = pDataTable.Columns.Count;
            DataColumn[] columns = pDataTable.PrimaryKey;
            string pkClmnName = columns[0].ColumnName;

            string tableName = pDataTable.GetType().GetField("TableFullName").GetValue(pDataTable).ToString();
            string crudType = pCrudType;
            DateTime crudDate = DateTime.Now;
            string pkValue = string.Empty;

            foreach (DataRow rw in pDt.Rows)
            {
                string iudValues = string.Empty;
                foreach (DataColumn clm in pDt.Columns)
                {
                    string clmName = clm.ColumnName;
                    if (clmName == pkClmnName)
                    {
                        pkValue = rw[clm].ToString();
                        if (pCrudType == "I") continue;
                    }
                    iudValues = string.Format("{0},{1}={2}", iudValues, clmName, rw[clm].ToString());
                }
                iudValues = iudValues.TrimStart(',');

                switch (mDefaultDB)
                {
                    case DEFAULT_DB_NAME:
                        CRUD_LOGDataSet.CRUD_LOGRow logDrw1 = ((CRUD_LOGDataSet)pLogDs).CRUD_LOG.NewCRUD_LOGRow();
                        logDrw1.CrudUserID = ProcessorUserID;
                        logDrw1.CrudTableName = tableName;
                        logDrw1.CrudType = crudType;
                        logDrw1.CrudDate = crudDate;
                        logDrw1.PKValue = pkValue;
                        logDrw1.CrudValues = iudValues;

                        ((CRUD_LOGDataSet)pLogDs).CRUD_LOG.AddCRUD_LOGRow(logDrw1);
                        break;

                    case DB_NAME_OTHER1:
                        break;

                    case DB_NAME_OTHER2:
                        break;
                }
            }
        }

        private string GetIdentityColumnName(DataTable pDataTable)
        {
            List<string> mColNames = (from DataColumn o in pDataTable.Columns
                                      where o.AutoIncrement == true
                                      select o.ColumnName).ToList();
            if (mColNames.Count == 0)
                return string.Empty;
            else
                return mColNames[0];
        }

        #endregion

        #region [EXCEPTION HANDLING] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

        private void HandleException(Exception pException, DataTable pDataTable)
        {
            //If there is a sql query, it is added to exception object
            if (pDataTable != null)
                if (pException.Data["SQL"] == null) pException.Data.Add("SQL", pDataTable.TableName + " tablosu TypeDataSet ile guncellenirken veya veri girilirken hata oluştu");

            //Caller method name is added to exception obeject
            StackTrace stackTrace = new StackTrace();
            string method = stackTrace.GetFrame(2).GetMethod().ToString();
            if (!string.IsNullOrEmpty(method) && !pException.Data.Contains("Method")) pException.Data.Add("Method", method);

            throw pException;
        }

        private void HandleException(Exception pException, DbCommand pCommand)
        {
            //If there is a sql query, it is added to exception object
            if (pCommand != null) pException.Data.Add("SQL", pCommand.CommandText);

            //Caller method name is added to exception obeject
            StackTrace stackTrace = new StackTrace();
            string method = stackTrace.GetFrame(2).GetMethod().ToString();
            if (!string.IsNullOrEmpty(method) && !pException.Data.Contains("Method")) pException.Data.Add("Method", method);

            throw pException;
        }

        #endregion

        #region [OTHERS] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~



        #endregion

    }
}
